home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / mmdf / mmdf-IIb.43 / lib / dial / d_packet.c < prev    next >
Encoding:
C/C++ Source or Header  |  1986-04-03  |  16.1 KB  |  704 lines

  1. #
  2. # include  "d_proto.h"
  3. # include  "d_returns.h"
  4. # include  <signal.h>
  5. # include  <stdio.h>
  6. # include  "d_structs.h"
  7. # include  "d_syscodes.h"
  8.  
  9. extern int d_master;
  10. extern int d_errno;
  11. extern int d_xretry;
  12. extern int d_nbuff;
  13. extern int d_toack;
  14. extern int d_rcvseq;
  15. extern int d_todata;
  16. static struct pkbuff packst[NBUFFMAX];
  17. static int first;        /*  index of first packet in buff  */
  18. static int nout;        /*  number of packets in buff      */
  19.  
  20. /*  Jun 81  D. Crocker      Major rework of alarm usage
  21.  *                          Converted some d_dbglogs to d_log
  22.  *                          fix sndpacket() timeout return value
  23.  *  Jul 81  D. Crocker      fix ack timeout to cause retransmit
  24.  *  Dec 81  D. Crocker      add retry hack to separate packets
  25.  *  Feb 84  D. Long         changed separating string to nn instead of nrnr
  26.  */
  27.  
  28.  
  29. /*
  30.  *     D_BLDPACK
  31.  *
  32.  *     routine which builds a packet for transmission.  most of the
  33.  *     understanding of the packet format is in this function.
  34.  *
  35.  *     type -- packet type code
  36.  *
  37.  *     seqnum -- packet sequence number
  38.  *
  39.  *     eof -- end of file indicator.  when non-zero, it causes the eof bit
  40.  *            to be set in the outgoing packet
  41.  *
  42.  *     text -- if non-zero, then this is taken to be a pointer to the text to
  43.  *             be included in the packet
  44.  *
  45.  *     buffer -- place to load the complete packet
  46.  */
  47.  
  48. d_bldpack (type, seqnum, eof, text, buffer)
  49. char    type,
  50.        *buffer;
  51. register char  *text;
  52. int     seqnum,
  53.         eof;
  54. {
  55.     register char  *cp;
  56.     register int    flags;
  57.     int     length;
  58.  
  59. #ifdef D_DBGLOG
  60.     d_dbglog ("d_bldpack", "building packet - type '%c', seqnum %d, eof %d",
  61.         type, seqnum, eof);
  62. #endif D_DBGLOG
  63.  
  64.     buffer[TYPEOFF] = type;
  65.  
  66.     /*  load the flag byte  */
  67.     flags = seqnum & 03;
  68.  
  69.     if (eof)
  70.     flags |= EOFBIT;
  71.  
  72.     if (d_master)
  73.     flags |= MASTERBIT;
  74.  
  75.     buffer[FLAGOFF] = d_tohex (flags);
  76.  
  77.     /*  load the text, if any  */
  78.     cp = &buffer[TEXTOFF];
  79.     length = 6;
  80.  
  81.     if (text)
  82.     while (*text)
  83.     {
  84.         *cp++ = *text++;
  85.         length++;
  86.     }
  87.  
  88.     /*  tack on the checksum and the line terminator  */
  89.     d_cscomp (buffer, length);
  90.  
  91.     *cp++ = '\r';
  92.     *cp++ = '\n';
  93.     /*  although the '\0' is not used by this package, it makes
  94.      *  printing easier.
  95.      */
  96.     *cp = '\0';
  97.     length += 2;
  98.  
  99.     return (length);
  100. }
  101.  
  102. /*
  103.  *
  104.  *     D_SNPKT
  105.  *
  106.  *     this routine sends a packet and waits for a given type response
  107.  *     packet from the other side.  retransmissions are done here.
  108.  *
  109.  *      type -- type of packet being sent; used to determine reply type.
  110.  *
  111.  *     packet -- pointer to fully formed packet
  112.  *
  113.  *     length -- length of the packet
  114.  *
  115.  */
  116.  
  117. d_snpkt (type, packet, length)
  118. char    type;
  119. char   *packet;
  120. int     length;
  121. {
  122.     register int    result;
  123.     int     next;
  124.  
  125. #ifdef D_DBGLOG
  126.     d_dbglog ("d_snpkt", "sending packet - code '%c'", packet[TYPEOFF]);
  127. #endif D_DBGLOG
  128.  
  129.     /*
  130.      * Should we wait for an outstanding packet before sending this one?
  131.      */
  132.  
  133.     if (type != DATAACK && nout == d_nbuff) {
  134.         result = d_confirm (&packst[first]);
  135.         /*  
  136.          * We always want to pull this msg off the front of
  137.          * the queue, even if the 'confirm' failed.  Ours is
  138.          * not to judge whether this is bad or not, but merely
  139.          * to indicate to the calling routine what happened.
  140.          * If it doesn't care that the packet wasn't ACKed,
  141.          * then we don't want to screw things up by leaving
  142.          * the packet in the queue.
  143.          */
  144.         first = (first + 1) % NBUFFMAX;
  145.         nout--;
  146.         switch (result)
  147.         {
  148.         case D_OK:
  149.             break;
  150.  
  151.         case D_FATAL:
  152.             /*
  153.              * Couldn't get an ACK on the last packet.
  154.              * Return an error.
  155.              */
  156.             return (D_FATAL);
  157.  
  158.         default:
  159. #ifdef D_LOG
  160.             d_log ("d_snpkt", "Unknown return (%d) from d_confirm", result);
  161. #endif D_LOG
  162.             return (D_FATAL);
  163.         }
  164.     }
  165.     /*
  166.      * We now have a space for this packet, store it.
  167.      */
  168.     next = (first + nout) % NBUFFMAX;
  169.     d_packset(type, packet, length, &packst[next]);
  170.     /*
  171.      * Send out the packet for the first time.
  172.      */
  173.     result = d_transmit(&packst[next]);
  174.     /*
  175.      * If this was an ACK packet, or if the write to the port failed,
  176.      * then there is no need to wait around for an ACK.  Note that in
  177.      * this case the packet was essentially never queued in the buffer
  178.      * because we never incremented 'nout'.
  179.      */
  180.     if ((result == D_OK) || (result < 0))
  181.         return (result);
  182.  
  183.     /* Update the information about outstanding packets */
  184.     nout++;
  185.  
  186.     /*
  187.      * In some cases the packet just sent must be ACKed immediately.
  188.      * These cases are: 1) if buffering is not allowed at all,
  189.      * 2) If this is an end-of-logical-record packet, or 3) if this
  190.      * is not a data packet (which implies end of record).
  191.      * Of course, if we are going to require that the last packet
  192.      * be ACKed, we had better get ACK's for all the ones preceding
  193.      * it.
  194.      *
  195.      * Performance improvement note:  the above is only strictly true
  196.      * for QUIT packets.  We can wait for the next transmitted
  197.      * packet to get the previous ACK in all other cases. -- DR
  198.      */
  199.     if (((d_fromhex (packst[next].p_data[FLAGOFF]) & EOFBIT) != 0) ||
  200.         (packst[next].p_data[TYPEOFF] != DATA)) {
  201.  
  202.         /*
  203.          * Get an immediate ACK for all outstanding packets
  204.          */
  205.         while (nout > 0) {
  206. #ifdef D_DBGLOG
  207.             d_dbglog ("d_snpkt", "nout = %d, pack index = %d", nout, first);
  208. #endif D_DBGLOG
  209.             result = d_confirm (&packst[first]);
  210.             /*  See note above for explanation of next two lines  */
  211.             first = (first + 1) % NBUFFMAX;
  212.             nout--;
  213.             switch (result) {
  214.             case D_OK:
  215.                 break;
  216.  
  217.             case D_FATAL:
  218.                 return (D_FATAL);
  219.  
  220.             default:
  221. #ifdef D_LOG
  222.                 d_log ("d_snpkt", "Unknown return (%d) from d_transmit",
  223.                         result);
  224. #endif D_LOG
  225.                 return (D_FATAL);
  226.             }
  227.         }
  228.         return (D_OK);
  229.     }
  230.  
  231.     /*  If we get here, then this packet has not yet been ACKed, but
  232.      *  we are going to fudge and return D_OK, planning all the while
  233.      *  to get the ACK later.  If this packet is never ACKed, then its
  234.      *  error will be returned on a later packet.
  235.      */
  236.     return (D_OK);
  237. }
  238.  
  239. /*
  240.  *
  241.  *    D_TRANSMIT
  242.  *
  243.  *    this routine does the actual work of transmitting the packet.
  244.  *    It returns a value indicating whether an ACK should be expected.
  245.  *
  246.  *    packet - a pointer to the packet structure
  247.  *
  248. */
  249.  
  250. d_transmit(pack)
  251.   struct pkbuff *pack;
  252.     {
  253.     register int result;
  254.  
  255. #ifdef D_DBGLOG
  256.     d_dbglog ("d_transmit", "transmitting '%s'", pack->p_data);
  257. #endif D_DBGLOG
  258.  
  259.     if ((result = d_wrtport (pack->p_data, pack->p_length)) < 0)
  260.     {
  261. #ifdef D_LOG
  262.     d_log ("d_transmit", "on packet [%c](%d)'%s'",
  263.         pack->p_pktype, pack->p_length, pack->p_data);
  264. #endif D_LOG
  265.     return (result);
  266.     }
  267.  
  268.     /*  If this is an ACK packet, then it doesn't get acknowledged  */
  269.     switch (pack->p_pktype)
  270.     {
  271.     case DATAACK:          /*  acknowledge DATA packet           */
  272.     case XPATHACK:        /*  acknowledge XPATH packet          */
  273.     case RPATHACK:        /*  acknowledge RPATH packet          */
  274.     case ESCAPEACK:       /*  acknowledge ESCAPE packet         */
  275.     case QUITACK:           /*  acknowledge QUIT packet           */
  276.     case NBUFFACK:          /*  acknowledge NBUFF packet        */
  277.         return (D_OK);
  278.  
  279.     default:
  280.         return (D_CONTIN);
  281.     }
  282. }
  283.  
  284. /*
  285.  *
  286.  *    D_PACKSET
  287.  *
  288.  *    type - the type code for this packet
  289.  *    packet - pointer to the start of the packet itself
  290.  *    length - length of the packet in chars
  291.  *    pack - a structure with data about the packet being sent
  292.  *
  293.  *
  294. */
  295.  
  296. d_packset (type, packet, length, pack)
  297.   char type;
  298.   char packet[];
  299.   int length;
  300.   struct pkbuff *pack;
  301.     {
  302.     register char *cp;
  303.     register int index;
  304.  
  305.     /*  transfer appropriate information to the structure  */
  306.     for (cp = pack->p_data, index = 0;  index < length;  index++)
  307.     *cp++ = packet[index];
  308.     /*  Makes printing easier if needed after an error  */
  309.     *cp = '\0';
  310.     pack->p_length = length;
  311.     pack->p_pktype = type;
  312.     pack->p_seqnum = d_fromhex (packet[FLAGOFF]) & 03;
  313.  
  314.     switch (type)
  315.     {
  316.     case DATA:           /*  data packet  */
  317.         /*  Remember, if data was sent, an ACK is being waited on  */
  318.         pack->p_timeo = d_toack;
  319.         pack->p_acktype = DATAACK;
  320.         return;
  321.  
  322.     case XPATH:       /*  transmit path packet  */
  323.         pack->p_timeo = XPAWAIT;
  324.         pack->p_acktype = XPATHACK;
  325.         return;
  326.  
  327.     case RPATH:       /*  receive path packet  */
  328.         pack->p_timeo = RPAWAIT;
  329.         pack->p_acktype = RPATHACK;
  330.         return;
  331.  
  332.     case ESCAPE:       /*  escape packet  */
  333.         pack->p_timeo = EACKWAIT;
  334.         pack->p_acktype = ESCAPEACK;
  335.         return;
  336.  
  337.     case QUIT:           /*  quit packet  */
  338.         pack->p_timeo = QACKWAIT;
  339.         pack->p_acktype = QUITACK;
  340.         return;
  341.  
  342.     case NBUFF:        /* nbuffer packet */
  343.         pack->p_timeo = NBACKWAIT;
  344.         pack->p_acktype = NBUFFACK;
  345.         return;
  346.     }
  347. }
  348.  
  349. /*
  350.  *
  351.  *    D_CONFIRM
  352.  *
  353.  *    this routine looks for the ACK for the given pack.  If
  354.  *    it fails to recv it in a reasonable time, it retransmits
  355.  *    the pack.
  356.  *
  357.  *    packet - a pointer to the struct containing info about the
  358.  *         packet we are trying to get ACKed.
  359.  *
  360. */
  361.  
  362. d_confirm (packet)
  363. struct pkbuff *packet;
  364. {
  365.     register int result, index;
  366.     register int retries = 0;
  367.  
  368.     /*  loop, checking for the ACK and trying a retransmit  */
  369.  
  370. #ifdef D_DBGLOG
  371.     d_dbglog("d_confirm", "Looking for ack of packet [%c](%d)'%s'", 
  372.         packet->p_pktype, packet->p_length, packet->p_data);
  373. #endif D_DBGLOG
  374.  
  375.     while (retries++ < d_xretry) {
  376.         /*  The packet was sent once before this routine was called.
  377.          *  Therfore, start off looking for the ACK.
  378.          */
  379.         result = d_getack (packet);
  380.  
  381.         if (result > 0)
  382.             return (result);
  383.  
  384.         if (result == D_RETRY) {
  385.             /*
  386.              * Because the later packets should've been dropped
  387.              * due to bad sequence numbers, we must retransmit
  388.              * this packet and its successors.
  389.              * Send newlines to clear out any leftover noise.
  390.              */
  391. #ifdef D_DBGLOG
  392.             d_dbglog("d_confirm", "Retransmitting %d packet[s]", nout);
  393. #endif D_DBGLOG
  394. #ifdef D_LOG
  395.             d_log ("d_confirm", "separating");
  396. #endif D_LOG
  397.             d_wrtport ("\n\n", 2);
  398.             for(index = 0;  index < nout;  index++) {
  399.                 if (d_transmit (&packst[(first+index) % NBUFFMAX]) < 0) {
  400. #ifdef D_DBGLOG
  401.                     d_dbglog("d_confirm", "Error on retransmission");
  402. #endif D_DBGLOG
  403.                     return (D_FATAL);
  404.                 }
  405.             }
  406.             continue;
  407.         }
  408.  
  409.         /*  An unknown error  */
  410. #ifdef D_DBGLOG
  411.         d_dbglog ("d_confirm", "Unknown error from 'getack()'");
  412. #endif D_DBGLOG
  413.         return (result);
  414.     }
  415.  
  416. #ifdef D_LOG
  417.     d_log ("d_confirm", "retransmissions (%d) exhausted", retries-1);
  418.     d_log ("d_confirm", "on packet [%c](%d)'%s'",
  419.         packet->p_pktype, packet->p_length, packet->p_data);
  420. #endif D_LOG
  421.     d_errno = D_NORESP;
  422.     return (D_FATAL);
  423. }
  424.  
  425.  
  426. /*
  427.  *
  428.  *    D_GETACK
  429.  *
  430.  *    Actually tries to get the ACK.  This routine will set up
  431.  *    the time out alarm.  It returns when the alarm sounds, or
  432.  *    when the ACK has been received.    
  433.  *
  434.  *    pack - a pointer to the structure representing the packet
  435.  *
  436. */
  437.  
  438. d_getack (pack)
  439.   struct pkbuff *pack;
  440.     {
  441.     char respbuff[MAXPACKET + 2];
  442.     register int recvseq, result;
  443.  
  444.     /*  read incoming packets and watch for errors.  interrupted
  445.      *  reads return, allowing the calling routine to retransmit.
  446.      *  Other errors cause an immediate return.
  447.      */
  448.     d_alarm (pack->p_timeo);
  449.  
  450. #ifdef D_DBGLOG
  451.     d_dbglog ("d_getack", "Wanted: ack type '%c', seqnum %d",
  452.                 pack->p_acktype, pack->p_seqnum);
  453. #endif D_DBGLOG
  454.     for ( ;; )
  455.     {
  456.     if ((result = d_rdport (respbuff)) < D_OK)
  457.     {
  458.         if (result == D_INTRPT)
  459.         /*  The ack timed out  */
  460.         return (D_RETRY);
  461.         return (result);
  462.     }
  463.  
  464.     /*  check the type and sequence number  */
  465.     if (respbuff[TYPEOFF] == pack->p_acktype)
  466.     {
  467.         recvseq = d_fromhex (respbuff[FLAGOFF]) & 03;
  468.  
  469.         if (recvseq == pack->p_seqnum)
  470.         {
  471. #ifdef D_DBGLOG
  472.         d_dbglog ("d_getack", "response received");
  473. #endif D_DBGLOG
  474.         return (D_OK);
  475.         }
  476. #ifdef D_LOG
  477.         else
  478.         d_log ("d_getack", "bad packet seq num '%s'", respbuff);
  479. #endif D_LOG
  480.     }
  481. #ifdef D_LOG
  482.     else
  483.         d_log ("d_getack", "bad packet type '%s'", respbuff);
  484. #endif D_LOG
  485.     }
  486.  
  487. }
  488.  
  489.  
  490.  
  491. /*
  492.  *
  493.  *     D_ACKPACK
  494.  *
  495.  *     this routine is called to acknowledge a received packet.  it builds
  496.  *     the ack and sends it.  an ack won't be acknowledged.
  497.  *
  498.  *     type -- the type of the packet to be acknowledged
  499.  *
  500.  *     seqnum -- the sequence number of the packet
  501.  *
  502.  *
  503.  *     return values:
  504.  *
  505.  *          D_NONFATAL -- returned if the received packet has an unknown type
  506.  */
  507.  
  508. d_ackpack (type, seqnum)
  509. char    type;
  510. register int    seqnum;
  511. {
  512.     char    ackbuff[MAXPACKET + 2];
  513.     register int    ackleng,
  514.                     result;
  515.  
  516. #ifdef D_DBGLOG
  517.     d_dbglog ("d_ackpack", "acknowledging packet type '%c', seqnum %d",
  518.         type, seqnum);
  519. #endif D_DBGLOG
  520.  
  521.     /*  switch on the type.  some packet don't get ack's sent  */
  522.     switch (type)
  523.     {
  524.     case DATAACK: 
  525.     case XPATHACK: 
  526.     case RPATHACK: 
  527.     case QUITACK: 
  528.     case ESCAPEACK: 
  529.     case NBUFFACK:
  530.         return (D_OK);
  531.     }
  532.  
  533.     /*
  534.      * this code produces a protocol change which acts to alleviate
  535.      * a protocol deadlock problem in which the peers got out of sync
  536.      * during the envelope-verification phase.
  537.      */
  538.     /* if this packet is too badly out of sequence, do not ack it */
  539.     if (seqnum == d_incseq(d_rcvseq))
  540.     {
  541. #ifdef D_DBGLOG
  542.     d_dbglog("d_ackpack", "packet way out of seq--no ack--d_rcvseq %d", d_rcvseq);
  543. #endif D_DBGLOG
  544.         return(D_OK);
  545.     }
  546.  
  547.     /* now, generate the proper ACK */
  548.     switch(type)
  549.     {
  550.     case DATA: 
  551.         ackleng = d_bldpack ((type = DATAACK), seqnum,
  552.                     1, (char *) 0, ackbuff);
  553.         break;
  554.  
  555.     case RPATH: 
  556.         ackleng = d_bldpack ((type = RPATHACK), seqnum,
  557.                     1, (char *) 0, ackbuff);
  558.         break;
  559.  
  560.     case XPATH: 
  561.         ackleng = d_bldpack ((type = XPATHACK), seqnum,
  562.                     1, (char *) 0, ackbuff);
  563.         break;
  564.  
  565.     case QUIT: 
  566.         ackleng = d_bldpack ((type = QUITACK), seqnum,
  567.                     1, (char *) 0, ackbuff);
  568.         break;
  569.  
  570.     case ESCAPE: 
  571.         ackleng = d_bldpack ((type = ESCAPEACK), seqnum,
  572.                     1, (char *) 0, ackbuff);
  573.         break;
  574.  
  575.     case NBUFF:
  576.         ackleng = d_bldpack ((type = NBUFFACK), seqnum,
  577.                     1, (char *) 0, ackbuff);
  578.         break;
  579.  
  580.     default: 
  581. #ifdef D_LOG
  582.         d_log ("d_ackpack", "unknown packet type '%c'", type);
  583. #endif D_LOG
  584.         return (D_NONFATAL);
  585.     }
  586.  
  587.     /*  send that pack that we've built  */
  588.     result = d_snpkt (type, ackbuff, ackleng);
  589.     return (result);
  590. }
  591.  
  592. /*
  593.  *
  594.  *     D_WATCH
  595.  *
  596.  *     this routine is called to watch the incoming packets for one of
  597.  *     a specified type.  all other packets, except QUIT's, are ignored.
  598.  *
  599.  *     packet -- place to load the received packet
  600.  *
  601.  *     type -- the type of the packet we're looking for
  602.  */
  603.  
  604. d_watch (packet, type)
  605. register char  *packet;
  606. char    type;
  607. {
  608.     register int    length;
  609.     int     recvseq;
  610.  
  611. #ifdef D_DBGLOG
  612.     d_dbglog ("d_watch", "watching for packet with code '%c'", type);
  613. #endif D_DBGLOG
  614.  
  615.     switch (type)          /* set timer, for delayed packets     */
  616.     {
  617.     case DATA:           /*  data packet                       */
  618.         d_alarm (d_todata);
  619.         break;
  620.  
  621.     case XPATH:           /*  transmit path packet              */
  622.         d_alarm (XPATHWAIT);
  623.         break;
  624.  
  625.     case RPATH:           /*  receive path packet               */
  626.         d_alarm (RPATHWAIT);
  627.         break;
  628.  
  629.     case ESCAPE:           /*  escape packet  */
  630.         d_alarm (ESCAPEWAIT);
  631.         break;
  632.  
  633.     case NBUFF:        /* windowsize packet */
  634.         d_alarm(NBUFFWAIT);
  635.         break;
  636.     }
  637.  
  638. /*  read packets and search.  QUIT's cause a return after acknowlegement  */
  639.  
  640.     d_rcvseq = d_incseq (d_rcvseq);
  641.  
  642.     for ( ;; )
  643.     {
  644.     length = d_rdport (packet);
  645.  
  646.     if (length < 0)
  647.     {
  648. #ifdef D_LOG
  649.         d_log ("d_watch", "bad watch read (%d)", length);
  650. #endif D_LOG
  651.         break;
  652.     }
  653.     recvseq = d_fromhex (packet[FLAGOFF]) & 03;
  654.  
  655.     if (recvseq != d_rcvseq)
  656.     {
  657. #ifdef D_LOG
  658.         d_log ("d_watch",
  659.             "packet out of sequence - type '%c', got %d, expected %d",
  660.             packet[TYPEOFF], recvseq, d_rcvseq);
  661.         d_log ("d_watch", "in '%s'", packet);
  662. #endif D_LOG
  663.         continue;
  664.     }
  665.  
  666.     if (packet[TYPEOFF] == type)
  667.         break;            /* success                            */
  668.  
  669.     /*  We have received a packet, but it is not the type we
  670.      *  wanted.  Let's try to interrpret it as a special control
  671.      *  packet.
  672.      */
  673.     switch (d_control (packet, length))
  674.     {
  675.         case D_FATAL:
  676.         return (D_FATAL);
  677.  
  678.         case D_OK:
  679.         case D_NONFATAL:
  680.         break;
  681.     }
  682.     }
  683.  
  684.     return (length);
  685. }
  686.  
  687. /*
  688.  *
  689.  *     D_INCSEQ
  690.  *
  691.  *     this routine increments the sequence number passed as the argument
  692.  *     and returns the result.
  693.  *
  694.  *     seqnum -- the sequence number to be incremented
  695.  */
  696.  
  697. d_incseq (seqnum)
  698. register int    seqnum;
  699. {
  700.  
  701.     seqnum = (seqnum + 1) & 03;
  702.     return (seqnum);
  703. }
  704.